﻿using CPAIS.Updater;
using System;
using System.Collections.Generic;
using System.Data;
using System.Text;
using System.Threading;
using System.Xml;

namespace CPAIS.Model
{
    /// <summary>
    /// Represents an event log model.
    /// </summary>
    public class LogModel
    {
        #region Variables in LogModel
        protected List<string> diagnosisTypes;
        protected List<string> observationTypes;
        protected List<string> actionTypes;
        protected List<Trace> traces;
        #endregion

        #region Initialize

        /// <summary>
        /// Default constructor.
        /// </summary>
        public LogModel() : this("")
        {            
        }

        /// <summary>
        /// Constructor with event log name.
        /// </summary>
        public LogModel(string logName)
        {
            this.LogName = logName;
            this.Valid = false;
            this.diagnosisTypes = new List<string>();
            this.observationTypes = new List<string>();
            this.actionTypes = new List<string>();
            this.traces = new List<Trace>();
        }

        /// <summary>
        /// Clears the existed information.
        /// </summary>
        public void Clear()
        {
            this.LogName = "";
            this.Valid = false;
            this.traces.Clear();
            this.actionTypes.Clear();
            this.observationTypes.Clear();
            this.diagnosisTypes.Clear();
        }

        #endregion

        #region Basic properties

        /// <summary>
        /// Gets event log name.
        /// </summary>
        public string LogName
        {
            get;
            set;
        }

        /// <summary>
        /// Gets the flag whether it is valid.
        /// </summary>
        public bool Valid
        {
            get;
            set;
        }

        #endregion

        #region Traces

        /// <summary>
        /// Gets the patient traces.
        /// </summary>
        public List<Trace> Traces
        {
            get { return traces; }
        }

        /// <summary>
        /// Gets the count of patient traces.
        /// </summary>
        public int TraceCount
        {
            get { return traces.Count; }
        }

        /// <summary>
        /// Gets patient trace by ID.
        /// </summary>
        /// <param name="pid">Patient ID</param>
        /// <returns>Patient trace with specified ID</returns>
        public Trace GetTrace(string pid)
        {
            foreach (var trace in traces)
            {
                if (trace.ID == pid)
                    return trace;
            }
            return null;
        }

        /// <summary>
        /// Adds patient trace if not existed (ID).
        /// </summary>
        /// <param name="trace">Patient trace</param>
        /// <param name="check">Check if the trace has been existed</param>
        public void AddTrace(Trace trace, bool check = true)
        {
            if (check)
            {
                foreach (var item in traces)
                {
                    if (item.ID == trace.ID)
                        return;
                }
            }
            traces.Add(trace);
        }

        #endregion

        #region Diagnoses

        /// <summary>
        /// Gets the diagnosis types, as it is referenced by traces, so we return the a copy.
        /// </summary>
        public string[] DiagnosisTypes
        {
            get { return diagnosisTypes.ToArray(); }
        }

        /// <summary>
        /// Gets the count of diagnosis types.
        /// </summary>
        public int DiagnosisTypeCount
        {
            get { return diagnosisTypes.Count; }
        }

        /// <summary>
        /// Gets the total number of diagnoses in current log model.
        /// </summary>
        public int TotalDiagnoses
        {
            get
            {
                int count = 0;
                foreach (var trace in traces)
                    count += trace.DiagnosisLength;
                return count;
            }
        }

        /// <summary>
        /// Gets the maximum length of diagnoses in each trace.
        /// </summary>
        public int MaxDiagnosisLength
        {
            get
            {
                int ret = int.MinValue;
                foreach (var trace in traces)
                {
                    int length = trace.DiagnosisLength;
                    if (ret < length)
                    {
                        ret = length;
                    }
                }
                return ret;
            }
        }

        /// <summary>
        /// Gets the minimum length of diagnoses in each trace.
        /// </summary>
        public int MinDiagnosisLength
        {
            get
            {
                int ret = int.MaxValue;
                foreach (var trace in traces)
                {
                    int length = trace.DiagnosisLength;
                    if (ret > length)
                    {
                        ret = length;
                    }
                }
                return ret;
            }
        }

        /// <summary>
        /// Gets the average number of actions in each trace.
        /// </summary>
        public double AverageDiagnosisLength
        {
            get { return (double)TotalDiagnoses / traces.Count; }
        }

        #endregion

        #region Actions

        /// <summary>
        /// Gets the action types, as it is referenced by traces, so we return the a copy.
        /// </summary>
        public string[] ActionTypes
        {
            get { return actionTypes.ToArray(); }
        }

        /// <summary>
        /// Gets the count action types.
        /// </summary>
        public int ActionTypeCount
        {
            get { return actionTypes.Count; }
        }

        /// <summary>
        /// Gets the total number of actions in current log model.
        /// </summary>
        public int TotalActions
        {
            get
            {
                int count = 0;
                foreach (var trace in traces)
                    count += trace.Actions.Count;
                return count;
            }
        }

        /// <summary>
        /// Gets the maximum length of actions in each trace.
        /// </summary>
        public int MaxActionLength
        {
            get
            {
                int ret = int.MinValue;
                foreach (var trace in traces)
                {
                    int length = trace.Actions.Count;
                    if (ret < length)
                    {
                        ret = length;
                    }
                }
                return ret;
            }
        }

        /// <summary>
        /// Gets the minimum length of actions in each trace.
        /// </summary>
        public int MinActionLength
        {
            get
            {
                int ret = int.MaxValue;
                foreach (var trace in traces)
                {
                    int length = trace.Actions.Count;
                    if (ret > length)
                    {
                        ret = length;
                    }
                }
                return ret;
            }
        }

        /// <summary>
        /// Gets the average number of actions in each trace.
        /// </summary>
        public double AverageActionLength
        {
            get { return (double)TotalActions / traces.Count; }
        }

        /// <summary>
        /// Gets the days when at least an action occurred.
        /// </summary>
        public int[] ActionDays
        {
            get
            {
                List<int> ret = new List<int>();
                foreach (var trace in traces)
                {
                    foreach (var action in trace.Actions)
                    {
                        int day = action.Day;
                        if (!ret.Contains(day))
                        {
                            ret.Add(day);
                        }
                    }
                }
                ret.Sort();
                return ret.ToArray();
            }
        }

        #endregion

        #region Observations

        /// <summary>
        /// Gets the observation types, as it is referenced by traces, so we return the a copy.
        /// </summary>
        public string[] ObservationTypes
        {
            get { return observationTypes.ToArray(); }
        }

        /// <summary>
        /// Gets the count observation types.
        /// </summary>
        public int ObservationTypeCount
        {
            get { return observationTypes.Count; }
        }

        /// <summary>
        /// Gets the total number of observations in current log model.
        /// </summary>
        public int TotalObservations
        {
            get
            {
                int count = 0;
                foreach (var trace in traces)
                    count += trace.Observations.Count;
                return count;
            }
        }

        /// <summary>
        /// Gets the maximum length of observations in each trace.
        /// </summary>
        public int MaxObservationLength
        {
            get
            {
                int ret = int.MinValue;
                foreach (var trace in traces)
                {
                    int length = trace.Observations.Count;
                    if (ret < length)
                    {
                        ret = length;
                    }
                }
                return ret;
            }
        }

        /// <summary>
        /// Gets the minimum length of observations in each trace.
        /// </summary>
        public int MinObservationLength
        {
            get
            {
                int ret = int.MaxValue;
                foreach (var trace in traces)
                {
                    int length = trace.Observations.Count;
                    if (ret > length)
                    {
                        ret = length;
                    }
                }
                return ret;
            }
        }

        /// <summary>
        /// Gets the average number of observations in each trace.
        /// </summary>
        public double AverageObservations
        {
            get { return (double)TotalObservations / traces.Count; }
        }

        /// <summary>
        /// Gets the days when at least an observation occurred.
        /// </summary>
        public int[] ObservationDays
        {
            get
            {
                List<int> ret = new List<int>();
                foreach (var trace in traces)
                {
                    foreach (var observation in trace.Observations)
                    {
                        int day = observation.Day;
                        if (!ret.Contains(day))
                        {
                            ret.Add(day);
                        }
                    }
                }
                ret.Sort();
                return ret.ToArray();
            }
        }

        #endregion

        #region  Length Of Stay (LOS)

        /// <summary>
        /// Gets the maximum length of stay (LOS).
        /// </summary>
        public int MaxLOS
        {
            get
            {
                int ret = int.MinValue;
                foreach (var trace in traces)
                {
                    int LOS = trace.LOS;
                    if (ret < LOS)
                    {
                        ret = LOS;
                    }
                }
                return ret;
            }
        }

        /// <summary>
        /// Gets the minimum length of stay (LOS).
        /// </summary>
        public int MinLOS
        {
            get
            {
                int ret = int.MaxValue;
                foreach (var trace in traces)
                {
                    int LOS = trace.LOS;
                    if (ret > LOS)
                    {
                        ret = LOS;
                    }
                }
                return ret;
            }
        }

        /// <summary>
        /// Gets the average length of stay (LOS).
        /// </summary>
        public double AverageLOS
        {
            get
            {
                int ret = 0;
                foreach (var trace in traces)
                {
                    ret += trace.LOS;
                }
                return (double)ret / traces.Count;
            }
        }

        #endregion

        #region Used for Building LogModel INTERNAL

        /// <summary>
        /// Add diagnosis type, called when parsing log file.
        /// </summary>
        /// <param name="typeName">Diagnosis type name</param>
        /// <returns>The index of diagnosis, saved in Trace instance</returns>
        internal int AddDiagnosisType(string typeName)
        {
            typeName = typeName.Replace('\'', ' ');
            typeName = typeName.Replace('\"', ' ');
            if (diagnosisTypes.Contains(typeName))
                return diagnosisTypes.IndexOf(typeName);
            else
            {
                diagnosisTypes.Add(typeName);
                return diagnosisTypes.Count - 1;
            }
        }

        /// <summary>
        /// Gets diagnosis type name by index.
        /// </summary>
        /// <param name="index">Diagnosis type index in Trace instance</param>
        /// <returns>Diagnosis type name</returns>
        internal string GetDiagnosisType(int index)
        {
            return diagnosisTypes[index];
        }

        /// <summary>
        /// Add observation type, called when parsing log file.
        /// </summary>
        /// <param name="typeName">Observation type name</param>
        /// <returns>The index of observation, saved in ObservationEvent instance</returns>
        internal int AddObservationType(string typeName)
        {
            typeName = typeName.Replace('\'', ' ');
            typeName = typeName.Replace('\"', ' ');
            if (observationTypes.Contains(typeName))
                return observationTypes.IndexOf(typeName);
            else
            {
                observationTypes.Add(typeName);
                return observationTypes.Count - 1;
            }
        }

        /// <summary>
        /// Gets observation type name by index.
        /// </summary>
        /// <param name="index">Observation type index in ObservationEvent instance</param>
        /// <returns>Observation type name</returns>
        internal string GetObservationType(int index)
        {
            return observationTypes[index];
        }

        /// <summary>
        /// Gets acation type name by index.
        /// </summary>
        /// <param name="index">Action type index in ActionEvent instance</param>
        /// <returns>Action type name</returns>
        internal int AddActionType(string typeName)
        {
            typeName = typeName.Replace('\'', ' ');
            typeName = typeName.Replace('\"', ' ');
            if (actionTypes.Contains(typeName))
                return actionTypes.IndexOf(typeName);
            else
            {
                actionTypes.Add(typeName);
                return actionTypes.Count - 1;
            }
        }

        /// <summary>
        /// Gets diagnosis type name by index.
        /// </summary>
        /// <param name="index">Diagnosis type index in Trace instance</param>
        /// <returns>Diagnosis type name</returns>
        internal string GetActionType(int index)
        {
            return actionTypes[index];
        }

        #endregion

        #region Serializing and Deserializing

        /// <summary>
        /// Helper class for log model serialization.
        /// </summary>
        private class LogSerializer
        {
            private const string _LogModel = "LogModel";
            private const string _LogName = "LogName";
            private const string _KnowledgeBase = "KnowledgeBase";
            private const string _ActionTypes = "ActionTypes";
            private const string _ObservationTypes = "ObservationTypes";
            private const string _DiagnosisTypes = "DiagnosisTypes";
            private const string _Item = "Item";
            private const string _ID = "ID";
            private const string _Name = "Name";
            private const string _Traces = "Traces";
            private const string _Trace = "Trace";
            private const string _Sex = "Sex";
            private const string _Department = "Department";
            private const string _Cost = "Cost";
            private const string _Birthday = "Birthday";
            private const string _Admit = "Admit";
            private const string _Discharge = "Discharge";
            private const string _Actions = "Actions";
            private const string _Observations = "Observations";
            private const string _Diagnoses = "Diagnoses";
            private const string _Action = "Action";
            private const string _Observation = "Observation";
            private const string _Diagnosis = "Diagnosis";
            private const string _Index = "Index";
            private const string _TimestampAvaiable = "TimestampAvaiable";
            private const string _Timestamp = "Timestamp";
            private const string _Day = "Day";

            public static void Serialize(LogModel logModel, string pathName, ProgressUpdater updater)
            {
                if (updater != null)
                    updater.Begin();

                try
                {
                    XmlWriterSettings setting = new XmlWriterSettings();
                    setting.Indent = true;
                    setting.IndentChars = "  ";
                    using (XmlWriter writer = XmlWriter.Create(pathName, setting))
                    {
                        writer.WriteStartDocument();
                        writer.WriteComment(" This XML file represents the event log model ");
                        writer.WriteStartElement(_LogModel);        // Start LogModel
                        writer.WriteAttributeString(_LogName, logModel.LogName);
                        #region KnowledgeBase
                        writer.WriteStartElement(_KnowledgeBase);   // Start KnowledgeBase
                        writer.WriteStartElement(_ActionTypes);     // Start ActionTypes
                        var actionTypes = logModel.ActionTypes;
                        for (int i = 0; i < actionTypes.Length; i++)
                        {
                            writer.WriteStartElement(_Item);        // Start Item
                            writer.WriteAttributeString(_ID, i.ToString());
                            writer.WriteAttributeString(_Name, actionTypes[i]);
                            writer.WriteEndElement();               // End Item
                        }
                        writer.WriteEndElement();                   // End ActionTypes
                        writer.WriteStartElement(_ObservationTypes);// Start ObservationTypes
                        var observationTypes = logModel.ObservationTypes;
                        for (int i = 0; i < observationTypes.Length; i++)
                        {
                            writer.WriteStartElement(_Item);        // Start Item
                            writer.WriteAttributeString(_ID, i.ToString());
                            writer.WriteAttributeString(_Name, observationTypes[i]);
                            writer.WriteEndElement();               // End Item
                        }
                        writer.WriteEndElement();                   // End ObservationTypes
                        writer.WriteStartElement(_DiagnosisTypes);// Start DiagnosisTypes
                        var diagnosisTypes = logModel.DiagnosisTypes;
                        for (int i = 0; i < diagnosisTypes.Length; i++)
                        {
                            writer.WriteStartElement(_Item);        // Start Item
                            writer.WriteAttributeString(_ID, i.ToString());
                            writer.WriteAttributeString(_Name, diagnosisTypes[i]);
                            writer.WriteEndElement();               // End Item
                        }
                        writer.WriteEndElement();                   // End DiagnosisTypes
                        writer.WriteEndElement();                   // End KnowledgeBase
                        #endregion
                        #region Traces
                        writer.WriteStartElement(_Traces);          // Start Traces

                        var traces = logModel.traces;
                        int traceLength = traces.Count;
                        for (int i = 0; i < traceLength; i++)
                        {
                            Trace trace = traces[i];
                            writer.WriteStartElement(_Trace);       // Start Trace
                            writer.WriteAttributeString(_ID, trace.ID);
                            writer.WriteAttributeString(_Sex, trace.Sex.ToString());
                            writer.WriteAttributeString(_Department, trace.Department);
                            writer.WriteAttributeString(_Birthday, trace.Birthday.ToString());
                            writer.WriteAttributeString(_Admit, trace.Admit.ToString());
                            writer.WriteAttributeString(_Discharge, trace.Discharge.ToString());
                            writer.WriteStartElement(_Actions);     // Start Actions
                            bool isTimestampAvaiable = true;
                            if (trace.Actions.Count != 0)
                                isTimestampAvaiable = trace.Actions[0].IsTimestampAvailable;
                            writer.WriteAttributeString(_TimestampAvaiable, isTimestampAvaiable.ToString());
                            foreach (var action in trace.Actions)
                            {
                                writer.WriteStartElement(_Action);  // Start Action
                                writer.WriteAttributeString(_Index, action.TypeIndex.ToString());
                                if (isTimestampAvaiable)
                                    writer.WriteAttributeString(_Timestamp, action.Timestamp.ToString());
                                else
                                    writer.WriteAttributeString(_Day, action.Day.ToString());
                                writer.WriteEndElement();           // End Action
                            }
                            writer.WriteEndElement();               // End Actions
                            writer.WriteStartElement(_Observations);     // Start Observations
                            if (trace.Observations.Count != 0)
                                isTimestampAvaiable = trace.Observations[0].IsTimestampAvailable;
                            writer.WriteAttributeString(_TimestampAvaiable, isTimestampAvaiable.ToString());
                            foreach (var observation in trace.Observations)
                            {
                                writer.WriteStartElement(_Observation); // Start Observation
                                writer.WriteAttributeString(_Index, observation.TypeIndex.ToString());
                                if (isTimestampAvaiable)
                                    writer.WriteAttributeString(_Timestamp, observation.Timestamp.ToString());
                                else
                                    writer.WriteAttributeString(_Day, observation.Day.ToString());
                                writer.WriteEndElement();               // End Observation
                            }
                            writer.WriteEndElement();               // End Observations
                            writer.WriteStartElement(_Diagnoses);   // Start Diagnoses
                            var diagnoses = trace.Diagnoses;
                            foreach (var diagnosis in diagnoses)
                            {
                                writer.WriteStartElement(_Diagnosis);   // Start Diagnosis
                                writer.WriteAttributeString(_Index, Array.IndexOf(diagnosisTypes, diagnosis).ToString());
                                writer.WriteEndElement();               // End Diagnosis
                            }
                            writer.WriteEndElement();               // End Diagnoses
                            writer.WriteEndElement();               // End Trace

                            if (updater != null)
                                updater.UpdateProgress((double)(i + 1) / traceLength);
                        }
                        writer.WriteEndElement();                   // End Traces
                        #endregion
                        writer.WriteEndElement();                   // End LogModel
                        writer.WriteEndDocument();
                        writer.Flush();
                    }

                    if (updater != null)
                        updater.Complete();
                }
                catch (Exception ex)
                {
                    if (updater != null)
                    {
                        if (updater.State == UpdateState.Cancelled)
                            return;
                        else
                            updater.Fail(ex.Message);
                    }
                    else
                        throw ex;
                }
            }

            public static void Deserialize(LogModel logModel, string pathName, StateUpdater updater)
            {
                if (updater != null)
                    updater.Begin();

                try
                {
                    using (XmlReader reader = XmlReader.Create(pathName))
                    {
                        while (reader.Read())
                        {
                            if (reader.NodeType == XmlNodeType.Element && reader.Name == _LogModel)
                            {
                                Dictionary<int, string> actionTypes = new Dictionary<int, string>();
                                Dictionary<int, string> observationTypes = new Dictionary<int, string>();
                                Dictionary<int, string> diagnosisTypes = new Dictionary<int, string>();
                                logModel.Clear();

                                reader.MoveToFirstAttribute();
                                logModel.LogName = reader.ReadContentAsString();

                                while (reader.Read())
                                {
                                    if (reader.Depth < 1)
                                        break;
                                    else if (reader.Depth == 1 && reader.NodeType == XmlNodeType.Element)
                                    {
                                        if (reader.Name == _KnowledgeBase)
                                        {
                                            #region KnowledgeBase
                                            while (reader.Read())
                                            {
                                                if (reader.Depth < 2)
                                                    break;
                                                else if (reader.Depth == 2 && reader.NodeType == XmlNodeType.Element)
                                                {
                                                    if (reader.Name == _ActionTypes)
                                                    {
                                                        while (reader.Read())
                                                        {
                                                            if (reader.Depth < 3)
                                                                break;
                                                            else if (reader.Depth == 3 && reader.NodeType == XmlNodeType.Element && reader.Name == _Item)
                                                            {
                                                                int id = -1;
                                                                string name = "";
                                                                while (reader.MoveToNextAttribute())
                                                                {
                                                                    if (reader.Name == _ID)
                                                                        id = reader.ReadContentAsInt();
                                                                    else if (reader.Name == _Name)
                                                                        name = reader.ReadContentAsString();
                                                                }
                                                                actionTypes.Add(id, name);
                                                            }
                                                        }
                                                    }
                                                    else if (reader.Name == _ObservationTypes)
                                                    {
                                                        while (reader.Read())
                                                        {
                                                            if (reader.Depth < 3)
                                                                break;
                                                            else if (reader.Depth == 3 && reader.NodeType == XmlNodeType.Element && reader.Name == _Item)
                                                            {
                                                                int id = -1;
                                                                string name = "";
                                                                while (reader.MoveToNextAttribute())
                                                                {
                                                                    if (reader.Name == _ID)
                                                                        id = reader.ReadContentAsInt();
                                                                    else if (reader.Name == _Name)
                                                                        name = reader.ReadContentAsString();
                                                                }
                                                                observationTypes.Add(id, name);
                                                            }
                                                        }
                                                    }
                                                    else if (reader.Name == _DiagnosisTypes)
                                                    {
                                                        while (reader.Read())
                                                        {
                                                            if (reader.Depth < 3)
                                                                break;
                                                            else if (reader.Depth == 3 && reader.NodeType == XmlNodeType.Element && reader.Name == _Item)
                                                            {
                                                                int id = -1;
                                                                string name = "";
                                                                while (reader.MoveToNextAttribute())
                                                                {
                                                                    if (reader.Name == _ID)
                                                                        id = reader.ReadContentAsInt();
                                                                    else if (reader.Name == _Name)
                                                                        name = reader.ReadContentAsString();
                                                                }
                                                                diagnosisTypes.Add(id, name);
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                            #endregion
                                        }
                                        else if (reader.Name == _Traces)
                                        {
                                            #region Traces
                                            while (reader.Read())
                                            {
                                                if (reader.Depth < 2)
                                                    break;
                                                else if (reader.Depth == 2 && reader.NodeType == XmlNodeType.Element && reader.Name == _Trace)
                                                {
                                                    #region Trace Information
                                                    string
                                                        id = "",
                                                        sex = "",
                                                        department = "";
                                                    double cost = 0.0;
                                                    DateTime
                                                        birthday = DateTime.MinValue,
                                                        admit = DateTime.MinValue,
                                                        discharge = DateTime.MinValue;
                                                    while (reader.MoveToNextAttribute())
                                                    {
                                                        switch (reader.Name)
                                                        {
                                                            case _ID:
                                                                id = reader.ReadContentAsString();
                                                                break;
                                                            case _Sex:
                                                                sex = reader.ReadContentAsString();
                                                                break;
                                                            case _Department:
                                                                department = reader.ReadContentAsString();
                                                                break;
                                                            case _Cost:
                                                                cost = reader.ReadContentAsDouble();
                                                                break;
                                                            case _Birthday:
                                                                birthday = DateTime.Parse(reader.ReadContentAsString());
                                                                break;
                                                            case _Admit:
                                                                admit = DateTime.Parse(reader.ReadContentAsString());
                                                                break;
                                                            case _Discharge:
                                                                discharge = DateTime.Parse(reader.ReadContentAsString());
                                                                break;
                                                        }
                                                    }
                                                    Trace trace = new Trace(logModel, id);
                                                    trace.Sex = sex == "Male" ? SexType.Male : (sex == "Female" ? SexType.Female : SexType.Unknown);
                                                    trace.Department = department;
                                                    trace.Cost = cost;
                                                    trace.Birthday = birthday;
                                                    trace.Admit = admit;
                                                    trace.Discharge = discharge;
                                                    #endregion

                                                    while (reader.Read())
                                                    {
                                                        if (reader.Depth < 3)
                                                            break;
                                                        else if (reader.Depth == 3 && reader.NodeType == XmlNodeType.Element)
                                                        {
                                                            if (reader.Name == _Actions)
                                                            {
                                                                #region Actions
                                                                reader.MoveToFirstAttribute();
                                                                bool isTimestampAvailable = bool.Parse(reader.ReadContentAsString());
                                                                while (reader.Read())
                                                                {
                                                                    if (reader.Depth < 4)
                                                                        break;
                                                                    else if (reader.Depth == 4 && reader.NodeType == XmlNodeType.Element && reader.Name == _Action)
                                                                    {
                                                                        int index = -1;
                                                                        DateTime timestamp = DateTime.MinValue;
                                                                        int day = -1;
                                                                        while (reader.MoveToNextAttribute())
                                                                        {
                                                                            if (reader.Name == _Index)
                                                                                index = reader.ReadContentAsInt();
                                                                            else if (reader.Name == _Timestamp)
                                                                                timestamp = DateTime.Parse(reader.ReadContentAsString());
                                                                            else if (reader.Name == _Day)
                                                                                day = reader.ReadContentAsInt();
                                                                        }
                                                                        if (isTimestampAvailable)
                                                                            trace.AddAction(new ActionEvent(trace, timestamp, actionTypes[index]));
                                                                        else
                                                                            trace.AddAction(new ActionEvent(trace, day, actionTypes[index]));
                                                                    }
                                                                }
                                                                #endregion
                                                            }
                                                            else if (reader.Name == _Observations)
                                                            {
                                                                #region Observations
                                                                reader.MoveToFirstAttribute();
                                                                bool isTimestampAvailable = bool.Parse(reader.ReadContentAsString());
                                                                while (reader.Read())
                                                                {
                                                                    if (reader.Depth < 4)
                                                                        break;
                                                                    else if (reader.Depth == 4 && reader.NodeType == XmlNodeType.Element && reader.Name == _Observation)
                                                                    {
                                                                        int index = -1;
                                                                        DateTime timestamp = DateTime.MinValue;
                                                                        int day = -1;
                                                                        while (reader.MoveToNextAttribute())
                                                                        {
                                                                            if (reader.Name == _Index)
                                                                                index = reader.ReadContentAsInt();
                                                                            else if (reader.Name == _Timestamp)
                                                                                timestamp = DateTime.Parse(reader.ReadContentAsString());
                                                                            else if (reader.Name == _Day)
                                                                                day = reader.ReadContentAsInt();
                                                                        }
                                                                        if (isTimestampAvailable)
                                                                            trace.AddObservation(new ObservationEvent(trace, timestamp, observationTypes[index]));
                                                                        else
                                                                            trace.AddObservation(new ObservationEvent(trace, day, observationTypes[index]));
                                                                    }
                                                                }
                                                                #endregion
                                                            }
                                                            else if (reader.Name == _Diagnoses)
                                                            {
                                                                #region Diagnoses
                                                                while (reader.Read())
                                                                {
                                                                    if (reader.Depth < 4)
                                                                        break;
                                                                    else if (reader.Depth == 4 && reader.NodeType == XmlNodeType.Element && reader.Name == _Observation)
                                                                    {
                                                                        reader.MoveToFirstAttribute();
                                                                        trace.AddDiagnosis(diagnosisTypes[reader.ReadContentAsInt()]);
                                                                    }
                                                                }
                                                                #endregion
                                                            }
                                                        }
                                                    }
                                                    trace.OrderTrace();
                                                    logModel.AddTrace(trace);
                                                    if (updater != null)
                                                        updater.UpdateMessage(string.Format("{0} traces have been loaded...", logModel.TraceCount));
                                                }
                                            }
                                            #endregion
                                        }
                                    }
                                }
                            }
                        }
                    }
                    logModel.Valid = true;
                    if (updater != null)
                        updater.Complete();
                }
                catch (Exception ex)
                {
                    if (updater != null)
                    {
                        if (updater.State == UpdateState.Cancelled)
                            return;
                        else
                            updater.Fail(ex.Message);
                    }
                    else
                        throw ex;
                }
            }
        }

        /// <summary>
        /// Serializes the event log model to XML file.
        /// </summary>
        /// <param name="pathName">Target XML file path name</param>
        public void Serialize(string pathName, ProgressUpdater updater)
        {
            LogSerializer.Serialize(this, pathName, updater);
        }

        /// <summary>
        /// Deserializes the event log model from XML file.
        /// </summary>
        /// <param name="pathName">Target XML file path name</param>
        public void Deserialze(string pathName, StateUpdater updater)
        {
            LogSerializer.Deserialize(this, pathName, updater);
        }

        #endregion
    }
}
